﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace élénkPróbája.native.@select
{
    public class PropValidatorSelector : ILitValidatorSelector
    {
        readonly IEnumerable<string> propNames;

        /// <summary>
        /// Creates a new instance of MemberNameValidatorSelector.
        /// </summary>
        public PropValidatorSelector(IEnumerable<string> propertyNames)
        {
            this.propNames = propertyNames;
        }

        /// <summary>
        /// Determines whether or not a rule should execute.
        /// </summary>
        /// <param name="rule">The rule</param>
        /// <param name="propertyPath">Property path (eg Customer.Address.Line1)</param>
        /// <param name="context">Contextual information</param>
        /// <returns>Whether or not the validator can execute.</returns>
        public bool CanExecute(IValidationRule rule, string propertyPath, ValidationContext context)
        {
            // Validator selector only applies to the top level.
            // If we're running in a child context then this means that the child validator has already been selected
            // Because of this, we assume that the rule should continue (ie if the parent rule is valid, all children are valid)
            return context.IsChildContext || propNames.Any(x => x == propertyPath || propertyPath.StartsWith(x + "."));
        }

        ///<summary>
        /// Creates a MemberNameValidatorSelector from a collection of expressions.
        ///</summary>
        public static PropValidatorSelector FromExpressions<T>(params Expression<Func<T, object>>[] propertyExpressions)
        {
            var members = propertyExpressions.Select(MemberFromExpression).ToList();
            return new PropValidatorSelector(members);
        }

        private static string MemberFromExpression<T>(Expression<Func<T, object>> expression)
        {
            var chain = PropChain.FromExpression(expression);

            if (chain.Count == 0)
            {
                throw new ArgumentException($"Expression '{expression}' does not specify a valid property or field.");
            }

            return chain.ToString();
        } 
    }
}